/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.security.shiro.filter.mgt;

import com.google.common.collect.Maps;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.NamedFilterList;

import javax.servlet.FilterConfig;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 对于每个请求
 * AbstractShiroFilter#doFilterInternal --> executeChain --> getExecutionChain --> resolver.getChain --> getFilterChainManager() --> matches(getChainNames)
 *
 * @author vacoor
 */
public class CustomFilterChainManager extends DefaultFilterChainManager {
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    private Map<String, String> defaultFilterChainDefinitionMap;
    private Map<String, String> dynamicFilterChainDefinitionMap;
    private boolean dynamicChainFirst = true;

    public CustomFilterChainManager() {
        defaultFilterChainDefinitionMap = Maps.newLinkedHashMap();
        dynamicFilterChainDefinitionMap = Maps.newLinkedHashMap();
    }

    public CustomFilterChainManager(FilterConfig filterConfig) {
        super(filterConfig);
        defaultFilterChainDefinitionMap = Maps.newLinkedHashMap();
        dynamicFilterChainDefinitionMap = Maps.newLinkedHashMap();
    }

    public void updateFilterChain() {
        writeLock.lock();
        final Map<String, NamedFilterList> origFilterChains = Maps.newLinkedHashMap(getFilterChains());
        try {

            Map<String, String> dynamicChains = getDynamicFilterChainDefinitionMap();
            Map<String, String> defaultChains = getDefaultFilterChainDefinitionMap();

            getFilterChains().clear();
            if (dynamicChainFirst) {
                createChain(dynamicChains);
                createChain(defaultChains);
            } else {
                createChain(defaultChains);
                createChain(dynamicChains);
            }
        } catch (RuntimeException ex) {
            setFilterChains(origFilterChains);
            throw ex;
        } finally {
            writeLock.unlock();
        }
    }

    protected void createChain(Map<String, String> chainDefinitionMap) {
        if (!CollectionUtils.isEmpty(chainDefinitionMap)) {
            for (Map.Entry<String, String> entry : chainDefinitionMap.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue();
                createChain(url, chainDefinition);
            }
        }
    }

    @Override
    public NamedFilterList getChain(String chainName) {
        readLock.lock();
        try {
            return super.getChain(chainName);
        } finally {
            readLock.unlock();
        }
    }


    @Override
    public Set<String> getChainNames() {
        readLock.lock();
        try {
            return super.getChainNames();
        } finally {
            readLock.unlock();
        }
    }


    public void setDynamicFilterChainDefinitionMap(Map<String, String> dynamicFilterChainDefinitionMap) {
        this.dynamicFilterChainDefinitionMap = dynamicFilterChainDefinitionMap;
    }

    public Map<String, String> getDynamicFilterChainDefinitionMap() {
        return dynamicFilterChainDefinitionMap;
    }

    public void setDefaultFilterChainDefinitionMap(Map<String, String> defaultFilterChainDefinitionMap) {
        this.defaultFilterChainDefinitionMap = defaultFilterChainDefinitionMap;
    }

    public Map<String, String> getDefaultFilterChainDefinitionMap() {
        return defaultFilterChainDefinitionMap;
    }

    public boolean isDynamicChainFirst() {
        return dynamicChainFirst;
    }

    public void setDynamicChainFirst(boolean dynamicChainFirst) {
        this.dynamicChainFirst = dynamicChainFirst;
    }

}
